package sim;

/**
 * 
 */

/**
 * The hazard controller class. Provides methods that check if a data hazard exists
 * and returns forwarded values if forwarding is possible.
 * @author Piotr
 *
 */
public class HazardController {
	
	
	/**
	 * Checks for a hazard given a source register regIndex. Looks at whether any
	 * instructions in the pipeline ahead of pipeStage are going to write to
	 * regIndex. Returns true if there is a hazard. Should only be called from
	 * the ID Stage before reading from registers.
	 * @param regIndex
	 * @return
	 */
	public boolean checkForHazard(int regIndex) {
		boolean hazardExists = false;
		Instruction instr;
		if (regIndex == 0) {
			return false;
		}
		instr = Simulator.exs.instr;
		if (instr != null && !instr.isNop()) {
			if (instr.dest1Reg == regIndex || instr.dest2Reg == regIndex) {
				hazardExists = true;
			}
		}
		instr = Simulator.mems.instr;
		if (instr != null && !instr.isNop()) {
			if (instr.dest1Reg == regIndex || instr.dest2Reg == regIndex) {
				hazardExists = true;
			}
		}
		return hazardExists;
	}
	
	
	/**
	 * Checks for a hazard, and if one exists, returns the necessary value if it
	 * is already available. If there is no hazard or the value is not available, 
	 * returns null. Should only be called from ID, EX, or MEM stages.
	 * @param regIndex	The register number to try to forward.
	 * @param stage The class of the stage that called this method.
	 * @return The value for regIndex if it is available, or null.
	 */
	public Integer tryForwarding(int regIndex, Class<?> stage) {
		String stageName = stage.getName();
		Integer fwVal = null;
		boolean hazardExists = false;
		Instruction instr;
		if (regIndex == 0) {
			return new Integer(0);
		}
		if (stageName.equals(Simulator.ids.getClass().getName())) {
			//Check MEMStage for dependences
			instr = Simulator.mems.instr;
			if (instr != null && !instr.isNop()) {
				if (instr.dest1Reg == regIndex) {
					fwVal = null;
					hazardExists = true;
					if (instr.dest1Ready == 0) {
						fwVal = new Integer(instr.getDest1Val());
					} 
				}
				if (instr.dest2Reg == regIndex) {
					hazardExists = true;
					fwVal = null;
					if (instr.dest2Ready == 0) {
						fwVal = new Integer(instr.getDest2Val());
					}
				}
			}
			//Check EXStage for dependences
			instr = Simulator.exs.instr;
			if (instr != null && !instr.isNop()) {
				if (instr.dest1Reg == regIndex) {
					fwVal = null;
					hazardExists = true;
					if (instr.dest1Ready == 0) {
						fwVal = new Integer(instr.getDest1Val());
					} 
				}
				if (instr.dest2Reg == regIndex) {
					fwVal = null;
					hazardExists = true;
					if (instr.dest2Ready == 0) {
						fwVal = new Integer(instr.getDest2Val());
					}
				}
			}
		}
		if (stageName.equals(Simulator.exs.getClass().getName())) {
			//Check WBStage for dependences
			instr = Simulator.wbs.instr;
			if (instr != null && !instr.isNop()) {
				if (instr.dest1Reg == regIndex) {
					hazardExists = true;
					fwVal = null;
					if (instr.dest1Ready == 0) {
						fwVal = new Integer(instr.getDest1Val());
					} 
				}
				if (instr.dest2Reg == regIndex) {
					hazardExists = true;
					fwVal = null;
					if (instr.dest2Ready == 0) {
						fwVal = new Integer(instr.getDest2Val());
					}
				}
			}
			//Check MEMStage for dependences
			instr = Simulator.mems.instr;
			if (instr != null && !instr.isNop()) {
				if (instr.dest1Reg == regIndex) {
					hazardExists = true;
					fwVal = null;
					if (instr.dest1Ready == 0) {
						fwVal = new Integer(instr.getDest1Val());
					} 
				}
				if (instr.dest2Reg == regIndex) {
					hazardExists = true;
					fwVal = null;
					if (instr.dest2Ready == 0) {
						fwVal = new Integer(instr.getDest2Val());
					}
				}
			}
		}
		if (stageName.equals(Simulator.mems.getClass().getName())) {
			//Check WBStage for dependences
			instr = Simulator.wbs.instr;
			if (instr != null && !instr.isNop()) {
				if (instr.dest1Reg == regIndex) {
					hazardExists = true;
					fwVal = null;
					if (instr.dest1Ready == 0) {
						fwVal = new Integer(instr.getDest1Val());
					} 
				}
				if (instr.dest2Reg == regIndex) {
					hazardExists = true;
					fwVal = null;
					if (instr.dest2Ready == 0) {
						fwVal = new Integer(instr.getDest2Val());
					}
				}
			}
		}
		
		if (hazardExists == false) {
			return null;
		} else {
			return fwVal;
		}
	}
	
	/**
	 * Causes a pipeline stall. Forces all pipe stages up to the calling stage to stall.
	 * @param stage	The class of the pipeline stage that caused the stall.
	 */
	public void stallPipe(Class<?> stage) {
		String stageName = stage.getName();
		if (stageName.equals(Simulator.ids.getClass().getName())) {
			//System.out.println("ID Stage caused a pipeline stall.");
			Simulator.ids.causedStall = true;
			Simulator.ids.stalled = true;
			Simulator.ifs.stalled = true;
		} else if (stageName.equals(Simulator.exs.getClass().getName())) {
			//System.out.println("EX Stage caused a pipeline stall.");
			Simulator.exs.causedStall = true;
			Simulator.exs.stalled = true;
			Simulator.ids.stalled = true;
			Simulator.ifs.stalled = true;
		} else {
			System.out.println("stallPipe called from invalid stage.");
			System.exit(1);
		}
	}
}
